<!--
 * @Descripttion: 基于ElementUI封装的table列表页组件（包含搜索、列表、分页功能）
 * @version: V1.0.0
 * @Author: huzhushan@126.com
 * @Date: 2021-02-04 13:24:16
 * @LastEditors: huzhushan@126.com
 * @LastEditTime: 2022-03-25 09:51:47
-->
<template>
  <div class="page-box">
    <div class="topbar">
      <!-- 搜索选项 -->
      <el-form
        v-if="!!search"
        ref="searchForm"
        class="search"
        :model="searchModel"
        :inline="true"
        label-position="right"
        :label-width="search.labelWidth"
      >
        <el-form-item
          v-for="item in search.fields"
          :key="item.name"
          :prop="item.name"
        >
          <slot v-if="item.type === 'custom'" :name="item.slot" />
          <el-select
            v-else-if="item.type === 'select'"
            v-model="searchModel[item.name]"
            :collapse-tags="!!item.isCollapseTags"
            :filterable="!!item.filterable"
            :multiple="!!item.multiple"
            clearable
            :default-first-option="!!item.defaultFirstOption"
            :loading="!!item.remote && remoteLoading"
            :remote="!!item.remote"
            :remote-method="
              !!item.remote ? remoteSearch.bind(null, item) : () => {}
            "
            :fit-input-width="true"
            :popper-append-to-body="false"
            :placeholder="
              item.placeholder || `${item.label.replace(/：/g, '')}`
            "
            :style="{ width: search.inputWidth, ...item.style }"
          >
            <el-option
              v-for="option of item.options"
              :key="option.value"
              :label="option.name"
              :value="option.value"
            ></el-option>
          </el-select>
          <el-radio-group
            v-else-if="item.type === 'radio'"
            v-model="searchModel[item.name]"
            :style="{ width: search.inputWidth, ...item.style }"
          >
            <el-radio
              v-for="option of item.options"
              :key="option.value"
              :label="option.value"
            >
              {{ option.name }}
            </el-radio>
          </el-radio-group>
          <el-radio-group
            v-else-if="item.type === 'radio-button'"
            v-model="searchModel[item.name]"
            :style="{ width: search.inputWidth, ...item.style }"
          >
            <el-radio-button
              v-for="option of item.options"
              :key="option.value"
              :label="option.value"
            >
              {{ option.name }}
            </el-radio-button>
          </el-radio-group>
          <el-checkbox-group
            v-else-if="item.type === 'checkbox'"
            v-model="searchModel[item.name]"
            :style="{ width: search.inputWidth, ...item.style }"
          >
            <el-checkbox
              v-for="option of item.options"
              :key="option.value"
              :label="option.value"
            >
              {{ option.name }}
            </el-checkbox>
          </el-checkbox-group>
          <el-checkbox-group
            v-else-if="item.type === 'checkbox-button'"
            v-model="searchModel[item.name]"
            :style="{ width: search.inputWidth, ...item.style }"
          >
            <el-checkbox-button
              v-for="option of item.options"
              :key="option.value"
              :label="option.value"
            >
              {{ option.name }}
            </el-checkbox-button>
          </el-checkbox-group>
          <el-date-picker
            v-else-if="item.type === 'date'"
            v-model="searchModel[item.name]"
            type="date"
            value-format="yyyy-MM-dd"
            :placeholder="`${item.label}`"
            :style="{ width: search.inputWidth, ...item.style }"
          ></el-date-picker>
          <el-date-picker
            v-else-if="item.type === 'datetime'"
            v-model="searchModel[item.name]"
            type="datetime"
            value-format="yyyy-MM-dd HH:mm:ss"
            clearable
            :placeholder="`${item.label}`"
            :style="{ width: search.inputWidth, ...item.style }"
          ></el-date-picker>
          <el-date-picker
            v-else-if="item.type === 'daterange'"
            v-model="searchModel[item.name]"
            type="daterange"
            value-format="yyyy-MM-dd"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            clearable
            :style="{ width: search.inputWidth, ...item.style }"
            @change="handleDateChange($event, item)"
          ></el-date-picker>
          <el-date-picker
            v-else-if="item.type === 'datetimerange'"
            v-model="searchModel[item.name]"
            type="datetimerange"
            value-format="yyyy-MM-dd HH:mm:ss"
            range-separator="-"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            clearable
            :style="{ width: search.inputWidth, ...item.style }"
            @change="handleDateChange($event, item)"
          ></el-date-picker>
          <el-input-number
            v-else-if="item.type === 'number'"
            v-model="searchModel[item.name]"
            :placeholder="`${item.label}`"
            controls-position="right"
            :min="item.min"
            :max="item.max"
            :style="{ width: search.inputWidth, ...item.style }"
          />
          <el-input
            v-else-if="item.type === 'textarea'"
            v-model="searchModel[item.name]"
            type="textarea"
            clearable
            :placeholder="
              item.placeholder || `${item.label.replace(/：/g, '')}`
            "
            :style="{ width: search.inputWidth, ...item.style }"
          ></el-input>
          <el-input
            v-else
            v-model="searchModel[item.name]"
            :placeholder="
              item.placeholder || `${item.label.replace(/：/g, '')}`
            "
            clearable
            :style="{ width: search.inputWidth, ...item.style }"
          ></el-input>
        </el-form-item>
        <el-form-item class="search-btn">
          <el-button-group>
            <el-tooltip effect="dark" content="查询" placement="top">
              <el-button
                type="primary"
                icon="el-icon-search"
                @click="handleSearch"
              ></el-button>
            </el-tooltip>
            <el-tooltip effect="dark" content="重置" placement="top">
              <el-button
                type="primary"
                icon="el-icon-refresh-right"
                @click="handleReset"
              ></el-button>
            </el-tooltip>
          </el-button-group>
        </el-form-item>
      </el-form>

      <!-- title 和 工具栏 -->
      <div v-if="!hideToolbar" class="toolbar">
        <slot name="toolbar"></slot>
      </div>
    </div>
    <!-- table表格栏 -->
    <div class="table">
      <el-table
        ref="table"
        v-loading="loading"
        size="default"
        :data="tableData"
        :row-key="rowKey"
        :tree-props="tree.treeProps"
        :lazy="tree.lazy"
        :load="tree.load"
        tooltip-effect="dark"
        stripe
        :border="border"
        highlight-current-row
        :height="height"
        @selection-change="handleSelectionChange"
        @expand-change="handleExpandChange"
      >
        <table-column v-for="item in columns" :key="item.label" :item="item">
          <template v-for="slotName in getAllSlots(item)" #[slotName]="scope">
            <slot :name="slotName" v-bind="scope"></slot>
          </template>
        </table-column>
      </el-table>
    </div>
    <!-- 分页 -->
    <el-pagination
      v-if="paginationConfig.show && total > 0"
      class="pagination"
      :style="paginationConfig.style"
      :current-page.sync="pageNum"
      :page-sizes="paginationConfig.pageSizes"
      :page-size.sync="pageSize"
      :layout="paginationConfig.layout"
      :total="total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    ></el-pagination>
  </div>
</template>
<script>
import TableColumn from './table-column.vue'
const getSearchModel = search => {
  const searchModel = {}
  if (search && search.fields) {
    search.fields.forEach(item => {
      switch (item.type) {
        case 'checkbox':
        case 'checkbox-button':
          searchModel[item.name] = []
          break
        default:
          break
      }
      if (item.defaultValue !== undefined) {
        searchModel[item.name] = item.defaultValue
        // 日期范围和时间范围真实变量默认值
        if (
          (item.type === 'daterange' || item.type === 'datetimerange') &&
          !!item.trueNames &&
          Array.isArray(item.defaultValue)
        ) {
          item.defaultValue.forEach((val, index) => {
            searchModel[item.trueNames[index]] = val
          })
        }
      }
    })
  }
  return searchModel
}
export default {
  components: { TableColumn },
  props: {
    // 请求数据的方法
    request: {
      type: Function,
      default: () => {},
    },
    // 表格标题
    title: {
      type: String,
      default: '',
    },
    height: {
      type: [String, Number, Object],
      default: null,
    },
    // 是否隐藏标题栏
    hideTitleBar: {
      type: Boolean,
      default: false,
    },
    // 是否隐藏按钮操作
    hideToolbar: {
      type: Boolean,
      default: false,
    },
    // 是否隐藏titleBar和toolBar
    hideHead: {
      type: Boolean,
      default: false,
    },
    // 搜索表单配置，false表示不显示搜索表单
    search: {
      type: [Boolean, Object],
      default: false,
    },
    border: {
      type: Boolean,
      default: false,
    },
    // 表头配置
    columns: {
      type: Array,
      default: () => [],
    },
    // 行数据的Key，同elementUI的table组件的row-key
    rowKey: {
      type: [String, Function],
      default: () => {},
    },
    // 分页配置，false表示不显示分页
    pagination: {
      type: [Boolean, Object],
      default: () => ({}),
    },
    initMethod: {
      type: Boolean,
      default: true,
    },
    tree: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    let paginationConfig = {
      show: false,
    }
    if (typeof this.pagination === 'object') {
      const { layout, pageSizes, style } = this.pagination
      paginationConfig = {
        show: true,
        layout: layout || 'total, sizes, prev, pager, next, jumper',
        pageSizes: pageSizes || [10, 20, 50, 100],
        style: style || {},
      }
    }

    return {
      searchModel: getSearchModel(this.search),
      loading: false,
      tableData: [],
      total: 0,
      pageNum: 1,
      pageSize: (!!this.pagination && this.pagination.pageSize) || 10,
      paginationConfig,
      isFold: this.search.isFold,
      remoteLoading: false,
    }
  },
  created() {
    // 请求列表数据
    this.initMethod && this.getTableData()
  },
  methods: {
    // 请求列表数据
    async getTableData() {
      this.loading = true
      const searchModel = this.optimizeFields(this.search)
      const { data, total } = await this.request({
        pageNum: this.pageNum,
        pageSize: this.pageSize,
        ...searchModel,
      })
      this.loading = false
      this.tableData = data
      this.total = total
    },
    // 搜索
    handleSearch() {
      this.pageNum = 1
      this.getTableData()
    },
    // 重置函数
    handleReset() {
      // if (JSON.stringify(this.searchModel) === '{}') {
      //   return
      // }
      this.$emit('reset')
      this.pageNum = 1
      this.searchModel = getSearchModel(this.search)
      this.getTableData()
    },
    // 刷新
    refresh() {
      this.getTableData()
    },
    // 优化搜索字段，
    // 1、如果搜索配置有transform处理函数，执行transform
    // 2、删除日期范围默认的name字段
    optimizeFields(search) {
      const searchModel = JSON.parse(JSON.stringify(this.searchModel))
      if (search && search.fields) {
        search.fields.forEach(item => {
          if (!searchModel.hasOwnProperty(item.name)) {
            return
          }
          if (item.transform) {
            searchModel[item.name] = item.transform(searchModel[item.name])
          }
          if (
            (item.type === 'daterange' || item.type === 'datetimerange') &&
            !!item.trueNames
          ) {
            delete searchModel[item.name]
          }
        })
      }
      return searchModel
    },
    // 当前页变化
    handleCurrentChange(page) {
      this.getTableData()
    },
    // 改变每页size数量
    handleSizeChange(value) {
      this.pageNum = 1
      this.getTableData()
    },
    // 全选
    handleSelectionChange(arr) {
      this.$emit('selectionChange', arr)
    },
    // 展开折叠
    handleExpandChange(row, expanded) {
      this.$emit('expandChange', row, expanded)
    },
    // 过滤方法
    filterHandler(value, row, column) {
      const property = column['property']
      return row[property] === value
    },
    // 日期范围
    handleDateChange(value, item) {
      if (!item.trueNames) {
        return
      }

      if (value) {
        value.forEach((val, index) => {
          this.searchModel[item.trueNames[index]] = val
        })
      } else {
        item.trueNames.forEach(key => {
          delete this.searchModel[key]
        })
      }
    },
    async remoteSearch(item, query) {
      console.log(2342)
      this.remoteLoading = true
      await item.remoteMethod(query)
      this.remoteLoading = false
    },
    getChildrenSlots(arr) {
      const res = []
      arr.forEach(item => {
        if (item.labelSlot) {
          res.push(item.labelSlot)
        }
        if (item.tdSlot) {
          res.push(item.tdSlot)
        }
        if (item.children) {
          res.push(...this.getChildrenSlots(item.children))
        }
      })

      return res
    },
    getAllSlots(item) {
      const res = []
      if (item.labelSlot) {
        res.push(item.labelSlot)
      }
      if (item.tdSlot) {
        res.push(item.tdSlot)
      }
      if (item.children) {
        res.push(...this.getChildrenSlots(item.children))
      }
      return res
    },
  },
}
</script>
<style lang="scss" scoped>
.page-box {
  width: 100%;
  box-sizing: border-box;
  .topbar {
    display: flex;
    justify-content: space-between;
  }
  .search {
    // padding: 20px 20px 0;
    background: #fff;
    position: relative;
    .el-form-item {
      margin-bottom: 16px;
    }
    .search-btn {
      ::v-deep .el-button-group {
        vertical-align: -11px;
      }
    }
    // .toggle-btn {
    //   display: inline-block;
    //   width: 20px;
    //   height: 20px;
    //   line-height: 18px;
    //   padding: 0;
    //   border: 1px solid #737784;
    //   box-sizing: border-box;
    //   border-radius: 50%;
    //   font-size: 10px;
    //   text-align: center;
    //   cursor: pointer;
    // }
    :deep(.el-input-number .el-input__inner) {
      text-align: left;
    }
  }
  .head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 0 0;
    background: #fff;
    .title {
      font-size: 16px;
    }
  }
  .table {
    padding: 0 0 16px;
    background: #fff;
    ::v-deep th {
      background: #fafafa;
      color: rgba(0, 0, 0, 0.85);
    }
  }
  .pagination {
    padding: 0 0 16px;
    background: #fff;
    text-align: right;
    :last-child {
      margin-right: 0;
    }
  }
  .fold {
    transition: all ease 200ms;
    &.opened {
      transform: rotate(180deg);
    }
  }
}
</style>
